package edu.napier.soc.xfdm.uci;

import com.csvreader.CsvWriter;
import java.awt.event.InputEvent;
import javax.swing.JMenuItem;
import javax.swing.JSeparator;
import javax.swing.JToggleButton;
import javax.swing.KeyStroke;
import javax.swing.SwingConstants;
import org.syntax.jedit.TextAreaDefaults;
import org.syntax.jedit.tokenmarker.XFDMTokenMarker;
import edu.napier.soc.xfdm.model.Database;
import edu.napier.soc.xfdm.parser.ExecuteException;
import edu.napier.soc.xfdm.parser.InterpreterException;
import edu.napier.soc.xfdm.parser.QueryResult;
import edu.napier.soc.xfdm.parser.QueryResultSet;
import edu.napier.soc.xfdm.parser.XFDMParser;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.Frame;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTabbedPane;
import javax.swing.JTable;
import javax.swing.JTextArea;
import javax.swing.JToolBar;
import javax.swing.JToolBar.Separator;
import javax.swing.SwingWorker;
import javax.swing.TransferHandler;
import javax.swing.WindowConstants;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.filechooser.FileFilter;
import javax.swing.filechooser.FileNameExtensionFilter;
import javax.swing.table.TableModel;
import javax.swing.table.TableRowSorter;
import org.syntax.jedit.JEditTextArea;

/**
 *
 * @author Thomas
 */
public class MainGUI extends javax.swing.JFrame {

	private Database d = new Database();
	private final XFDMParser xfdm = new XFDMParser(d);
	private ImageIcon icon;
	private List<JTable> tables = new ArrayList<JTable>();
	private List<JPanel> panels = new ArrayList<JPanel>();

    /** Creates new form MainGUI */
    public MainGUI() {
		icon = new ImageIcon(getClass().getResource("/edu/napier/soc/xfdm/" +
				"resources/icon.png"));
        initComponents();
		setLocationRelativeTo(null);
		editTextArea.requestFocusInWindow();
		this.setTransferHandler(handler);
		
    }

    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {
        GridBagConstraints gridBagConstraints;

        mainPanel = new JPanel();
        splitPane = new JSplitPane();
        statementsPanel = new JPanel();
        consolePanel = new JPanel();
        consoleScrollPane = new JScrollPane();
        consoleTextArea = new JTextArea();
		TextAreaDefaults tad = TextAreaDefaults.getDefaults();
        tad.lineHighlight = false;
        editTextArea = new JEditTextArea();
        tabbedPane = new JTabbedPane();
        tempPanel = new JPanel();
        progressPanel = new JPanel();
        progressBar = new JProgressBar();
        stopButton = new JButton();
        bottomPanel = new JPanel();
        csvButton = new JButton();
        expandButton = new JButton();
        toolBar = new JToolBar();
        executeButton = new JButton();
        clearButton = new JButton();
        loadStatementsButton = new JButton();
        separator = new Separator();
        dumpButton = new JButton();
        loadButton = new JButton();
        deleteButton = new JButton();
        seperator4 = new Separator();
        statementModeButton = new JToggleButton();
        seperator5 = new Separator();
        visualButton = new JButton();
        menuBar = new JMenuBar();
        fileMenu = new JMenu();
        openMenuItem = new JMenuItem();
        saveMenuItem = new JMenuItem();
        deleteMenuItem = new JMenuItem();
        seperator2 = new JSeparator();
        loadStatementsMenuItem = new JMenuItem();
        seperator3 = new JSeparator();
        jMenuItem2 = new JMenuItem();
        runMenu = new JMenu();
        executeMenuItem = new JMenuItem();
        clearMenuItem = new JMenuItem();
        toolsMenu = new JMenu();
        visualMenuItem = new JMenuItem();
        helpMenu = new JMenu();
        aboutMenuItem = new JMenuItem();

        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        setTitle("eXtended Functional Data Model");
        setIconImage(icon.getImage());
        setName("mainFrame"); // NOI18N

        mainPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
        mainPanel.setLayout(new GridBagLayout());

        splitPane.setBorder(null);
        splitPane.setDividerLocation(-5);
        splitPane.setDividerSize(15);
        splitPane.setOrientation(JSplitPane.VERTICAL_SPLIT);
        splitPane.setResizeWeight(0.6);
        splitPane.setOneTouchExpandable(true);

        statementsPanel.setPreferredSize(new Dimension(800, 600));
        statementsPanel.setLayout(new GridBagLayout());

        consolePanel.setBorder(BorderFactory.createTitledBorder("XFDM Console:"));
        consolePanel.setLayout(new BorderLayout());

        consoleTextArea.setEditable(false);
        consoleScrollPane.setViewportView(consoleTextArea);

        consolePanel.add(consoleScrollPane, BorderLayout.CENTER);

        gridBagConstraints = new GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 1;
        gridBagConstraints.fill = GridBagConstraints.BOTH;
        gridBagConstraints.weightx = 1.0;
        gridBagConstraints.weighty = 0.5;
        statementsPanel.add(consolePanel, gridBagConstraints);

        editTextArea.setBorder(BorderFactory.createEtchedBorder());
        editTextArea.setDoubleBuffered(true);
        editTextArea.setMaximumSize(new Dimension(600, 32767));
        editTextArea.setMinimumSize(new Dimension(4, 22));
        editTextArea.setPreferredSize(new Dimension(4, 22));
        editTextArea.setTokenMarker(new XFDMTokenMarker());
        editTextArea.addKeyListener(new KeyAdapter() {
            public void keyReleased(KeyEvent evt) {
                editTextAreaKeyReleased(evt);
            }
        });
        gridBagConstraints = new GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 0;
        gridBagConstraints.fill = GridBagConstraints.BOTH;
        gridBagConstraints.weightx = 1.0;
        gridBagConstraints.weighty = 1.0;
        statementsPanel.add(editTextArea, gridBagConstraints);

        splitPane.setLeftComponent(statementsPanel);

        tabbedPane.setTabPlacement(JTabbedPane.LEFT);
        tabbedPane.addChangeListener(new ChangeListener() {
            public void stateChanged(ChangeEvent evt) {
                tabbedPaneStateChanged(evt);
            }
        });

        tempPanel.setMaximumSize(new Dimension(0, 0));
        tempPanel.setMinimumSize(new Dimension(0, 0));
        tempPanel.setPreferredSize(new Dimension(0, 0));
        tabbedPane.addTab("blank", tempPanel);

        splitPane.setRightComponent(tabbedPane);

        gridBagConstraints = new GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 0;
        gridBagConstraints.fill = GridBagConstraints.BOTH;
        gridBagConstraints.weightx = 1.0;
        gridBagConstraints.weighty = 1.0;
        mainPanel.add(splitPane, gridBagConstraints);

        progressBar.setMinimumSize(progressBar.getPreferredSize());
        progressBar.setStringPainted(true);
        progressPanel.add(progressBar);

        stopButton.setText("stop");
        stopButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent evt) {
                stopButtonActionPerformed(evt);
            }
        });
        progressPanel.add(stopButton);

        progressPanel.setVisible(false);

        gridBagConstraints = new GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 2;
        gridBagConstraints.anchor = GridBagConstraints.LAST_LINE_END;
        mainPanel.add(progressPanel, gridBagConstraints);

        bottomPanel.setVisible(false);
        bottomPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));

        csvButton.setText("output to csv");
        csvButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent evt) {
                csvButtonActionPerformed(evt);
            }
        });
        bottomPanel.add(csvButton);

        expandButton.setText("expand");
        expandButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent evt) {
                expandButtonActionPerformed(evt);
            }
        });
        bottomPanel.add(expandButton);

        gridBagConstraints = new GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 1;
        gridBagConstraints.fill = GridBagConstraints.BOTH;
        mainPanel.add(bottomPanel, gridBagConstraints);

        getContentPane().add(mainPanel, BorderLayout.CENTER);

        toolBar.setRollover(true);

        executeButton.setIcon(new ImageIcon(getClass().getResource("/edu/napier/soc/xfdm/resources/run.png"))); // NOI18N
        executeButton.setToolTipText("Execute Statements (F5)");
        executeButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent evt) {
                executeButtonActionPerformed(evt);
            }
        });
        toolBar.add(executeButton);

        clearButton.setIcon(new ImageIcon(getClass().getResource("/edu/napier/soc/xfdm/resources/clear.png"))); // NOI18N
        clearButton.setToolTipText("Clear Statements");
        clearButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent evt) {
                clearButtonActionPerformed(evt);
            }
        });
        toolBar.add(clearButton);

        loadStatementsButton.setIcon(new ImageIcon(getClass().getResource("/edu/napier/soc/xfdm/resources/text.png"))); // NOI18N
        loadStatementsButton.setToolTipText("Open Statements...");
        loadStatementsButton.setFocusable(false);
        loadStatementsButton.setHorizontalTextPosition(SwingConstants.CENTER);
        loadStatementsButton.setVerticalTextPosition(SwingConstants.BOTTOM);
        loadStatementsButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent evt) {
                loadStatementsButtonActionPerformed(evt);
            }
        });
        toolBar.add(loadStatementsButton);
        toolBar.add(separator);

        dumpButton.setIcon(new ImageIcon(getClass().getResource("/edu/napier/soc/xfdm/resources/save.png"))); // NOI18N
        dumpButton.setToolTipText("Save Database... (CTRL + S)");
        dumpButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent evt) {
                dumpButtonActionPerformed(evt);
            }
        });
        toolBar.add(dumpButton);

        loadButton.setIcon(new ImageIcon(getClass().getResource("/edu/napier/soc/xfdm/resources/open.png"))); // NOI18N
        loadButton.setToolTipText("Open Database... (CTRL + O)");
        loadButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent evt) {
                loadButtonActionPerformed(evt);
            }
        });
        toolBar.add(loadButton);

        deleteButton.setIcon(new ImageIcon(getClass().getResource("/edu/napier/soc/xfdm/resources/delete.png"))); // NOI18N
        deleteButton.setToolTipText("Clear Database...");
        deleteButton.setFocusable(false);
        deleteButton.setHorizontalTextPosition(SwingConstants.CENTER);
        deleteButton.setVerticalTextPosition(SwingConstants.BOTTOM);
        deleteButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent evt) {
                deleteButtonActionPerformed(evt);
            }
        });
        toolBar.add(deleteButton);
        toolBar.add(seperator4);

        statementModeButton.setIcon(new ImageIcon(getClass().getResource("/edu/napier/soc/xfdm/resources/multi.png"))); // NOI18N
        statementModeButton.setToolTipText("Statement Mode (Single or Multi)");
        statementModeButton.setFocusable(false);
        statementModeButton.setHorizontalTextPosition(SwingConstants.CENTER);
        statementModeButton.setSelectedIcon(new ImageIcon(getClass().getResource("/edu/napier/soc/xfdm/resources/single.png"))); // NOI18N
        statementModeButton.setVerticalTextPosition(SwingConstants.BOTTOM);
        toolBar.add(statementModeButton);
        toolBar.add(seperator5);

        visualButton.setIcon(new ImageIcon(getClass().getResource("/edu/napier/soc/xfdm/resources/rels.png"))); // NOI18N
        visualButton.setToolTipText("Visual Viewer");
        visualButton.setFocusable(false);
        visualButton.setHorizontalTextPosition(SwingConstants.CENTER);
        visualButton.setVerticalTextPosition(SwingConstants.BOTTOM);
        visualButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent evt) {
                visualButtonActionPerformed(evt);
            }
        });
        toolBar.add(visualButton);

        getContentPane().add(toolBar, BorderLayout.PAGE_START);

        fileMenu.setText("File");
        fileMenu.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent evt) {
                fileMenuActionPerformed(evt);
            }
        });

        openMenuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O, InputEvent.CTRL_MASK));
        openMenuItem.setIcon(new ImageIcon(getClass().getResource("/edu/napier/soc/xfdm/resources/openS.png"))); // NOI18N
        openMenuItem.setText("Open Database...");
        openMenuItem.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent evt) {
                openMenuItemActionPerformed(evt);
            }
        });
        fileMenu.add(openMenuItem);

        saveMenuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, InputEvent.CTRL_MASK));
        saveMenuItem.setIcon(new ImageIcon(getClass().getResource("/edu/napier/soc/xfdm/resources/saveS.png"))); // NOI18N
        saveMenuItem.setText("Save As...");
        saveMenuItem.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent evt) {
                saveMenuItemActionPerformed(evt);
            }
        });
        fileMenu.add(saveMenuItem);

        deleteMenuItem.setIcon(new ImageIcon(getClass().getResource("/edu/napier/soc/xfdm/resources/deleteS.png"))); // NOI18N
        deleteMenuItem.setText("Clear Database...");
        deleteMenuItem.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent evt) {
                deleteMenuItemActionPerformed(evt);
            }
        });
        fileMenu.add(deleteMenuItem);
        fileMenu.add(seperator2);

        loadStatementsMenuItem.setIcon(new ImageIcon(getClass().getResource("/edu/napier/soc/xfdm/resources/textS.png"))); // NOI18N
        loadStatementsMenuItem.setText("Open Statements...");
        loadStatementsMenuItem.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent evt) {
                loadStatementsMenuItemActionPerformed(evt);
            }
        });
        fileMenu.add(loadStatementsMenuItem);
        fileMenu.add(seperator3);

        jMenuItem2.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_E, InputEvent.CTRL_MASK));
        jMenuItem2.setText("Exit");
        jMenuItem2.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent evt) {
                jMenuItem2ActionPerformed(evt);
            }
        });
        fileMenu.add(jMenuItem2);

        menuBar.add(fileMenu);

        runMenu.setText("Run");

        executeMenuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F5, 0));
        executeMenuItem.setIcon(new ImageIcon(getClass().getResource("/edu/napier/soc/xfdm/resources/playS.png"))); // NOI18N
        executeMenuItem.setText("Execute Statements");
        executeMenuItem.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent evt) {
                executeMenuItemActionPerformed(evt);
            }
        });
        runMenu.add(executeMenuItem);

        clearMenuItem.setIcon(new ImageIcon(getClass().getResource("/edu/napier/soc/xfdm/resources/clearS.png"))); // NOI18N
        clearMenuItem.setText("Clear Statements");
        clearMenuItem.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent evt) {
                clearMenuItemActionPerformed(evt);
            }
        });
        runMenu.add(clearMenuItem);

        menuBar.add(runMenu);

        toolsMenu.setText("Tools");

        visualMenuItem.setIcon(new ImageIcon(getClass().getResource("/edu/napier/soc/xfdm/resources/visualS.png"))); // NOI18N
        visualMenuItem.setText("Visual Viewer");
        visualMenuItem.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent evt) {
                visualMenuItemActionPerformed(evt);
            }
        });
        toolsMenu.add(visualMenuItem);

        menuBar.add(toolsMenu);

        helpMenu.setText("Help");

        aboutMenuItem.setText("About");
        aboutMenuItem.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent evt) {
                aboutMenuItemActionPerformed(evt);
            }
        });
        helpMenu.add(aboutMenuItem);

        menuBar.add(helpMenu);

        setJMenuBar(menuBar);

        pack();
    }// </editor-fold>//GEN-END:initComponents

	private void clearButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_clearButtonActionPerformed
			editTextArea.setText("");
			editTextArea.requestFocusInWindow();
}//GEN-LAST:event_clearButtonActionPerformed

	private void executeButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_executeButtonActionPerformed
		progressPanel.setVisible(true);
		new SwingThread(editTextArea.getText(), this).execute();
	}//GEN-LAST:event_executeButtonActionPerformed

	private void dumpButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_dumpButtonActionPerformed
		File f = saveFile(this,
				new FileNameExtensionFilter("XFDM Database (*.xfm)", "xfm"));

		if (f == null) {
			return;
		}
		if (!f.getName().toLowerCase().endsWith(".xfm")) {
			f = new File(f.getAbsolutePath() + ".xfm");
		}
		ObjectOutputStream out = null;

		try {
			try {
				out = new ObjectOutputStream(new FileOutputStream(f));
				out.writeObject(d);
			} finally {
				if (out != null) {
					out.close();
				}
			}
			consoleTextArea.setText("Database saved to disk.");
		} catch (IOException ex) {
			consoleTextArea.setText("Problem saving the database: "
					+ ex.getMessage());
		}
	}//GEN-LAST:event_dumpButtonActionPerformed

	public static File saveFile(Component parent, FileFilter... ff) {
		JFileChooser jfc = new JFileChooser(".");

		for (FileFilter f : ff) {
			jfc.addChoosableFileFilter(f);
		}
		jfc.setAcceptAllFileFilterUsed(false);
		int opt = jfc.showSaveDialog(parent);

		if (opt == JFileChooser.CANCEL_OPTION) {
			return null;
		}

		if (opt == JFileChooser.APPROVE_OPTION) {
			return jfc.getSelectedFile();
		}

		return null;
	}

	public static File openFile(Component parent, boolean acceptAll,
			FileFilter... ff) {
		JFileChooser jfc = new JFileChooser(".");

		jfc.setAcceptAllFileFilterUsed(acceptAll);
		for (FileFilter f : ff) {
			jfc.addChoosableFileFilter(f);
			jfc.setFileFilter(f);
		}
		int opt = jfc.showOpenDialog(parent);

		if (opt == JFileChooser.CANCEL_OPTION) {
			return null;
		}

		if (opt == JFileChooser.APPROVE_OPTION) {
			return jfc.getSelectedFile();
		}

		return null;
	}
	private void loadButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_loadButtonActionPerformed
		File f = openFile(this, false,
				new FileNameExtensionFilter("XFDM Database (*.xfm)", "xfm"));

		if (f == null || !f.exists()) {
			return;
		}

		loadDatabase(f);
	}//GEN-LAST:event_loadButtonActionPerformed

	private SwingWorker<Void, Void> loadDB;

	public void loadDatabase(final File f) {
		if (loadDB == null) {
			loadDB = new SwingWorker<Void, Void>() {
				private volatile String message;

				@Override
				protected Void doInBackground() throws Exception {
					ObjectInputStream in = null;

					try {
						try {
							in = new ObjectInputStream(new FileInputStream(f));
							d = (Database)in.readObject();
							xfdm.setDatabase(d);
						} finally {
							if (in != null) {
								in.close();
							}
						}
						message = "XFDM database loaded into memory.";
					} catch (ClassNotFoundException ex) {
						message = "Loading encountered an error: "
								+ ex.getMessage();
					} catch (IOException ex) {
						message = "Loading encountered an error: "
								+ ex.getMessage();
					} catch (Exception ex) {
						ex.printStackTrace();
					}
					return null;
				}

				@Override
				protected void done() {
					progressPanel.setVisible(false);
					progressBar.setIndeterminate(false);
					progressBar.setStringPainted(true);
					stopButton.setVisible(true);
					consoleTextArea.setText(message);
				}
			};
		}

		switch (loadDB.getState()) {
				case STARTED:
					return;
				case DONE:
				case PENDING:
					progressPanel.setVisible(true);
					progressBar.setIndeterminate(true);
					progressBar.setStringPainted(false);
					stopButton.setVisible(false);
					loadDB.execute();
			}
			return;
	}
	private void stopButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_stopButtonActionPerformed
		xfdm.stopRequest();
	}//GEN-LAST:event_stopButtonActionPerformed

	private void tabbedPaneStateChanged(ChangeEvent evt) {//GEN-FIRST:event_tabbedPaneStateChanged
		bottomPanel.setVisible(tabbedPane.getTabCount() > 0);
	}//GEN-LAST:event_tabbedPaneStateChanged

	private void expandButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_expandButtonActionPerformed
		final int index = panels.indexOf(tabbedPane.getSelectedComponent());
		final JFrame jf =
				new JFrame("Query " + (index + 1));
		jf.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
		jf.setIconImage(icon.getImage());
		jf.add(tabbedPane.getSelectedComponent());
		jf.addWindowListener(new WindowAdapter() {
			
			@Override
			public void windowClosing(WindowEvent e) {
				if (tabbedPane.getTabCount() >= index) {
					tabbedPane.insertTab("Query " + (index + 1), null,
						panels.get(index), null, index);
				} else {
					tabbedPane.addTab("Query " + (index + 1), panels.get(index));
				}
			}
		});
		jf.pack();
		jf.setVisible(true);
	}//GEN-LAST:event_expandButtonActionPerformed

	private void csvButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_csvButtonActionPerformed
		JTable jt = tables.get(tabbedPane.getSelectedIndex());
		File f = saveFile(this, new FileNameExtensionFilter("CSV File (*.csv)", "csv"));

		if (f == null) {
			return;
		}

		if (!f.getName().toLowerCase().endsWith(".csv")) {
			f = new File(f.getAbsolutePath() + ".csv");
		}
		CsvWriter csv = new CsvWriter(f.getAbsolutePath());
		TableModel dtm = jt.getModel();

		try {
			String[] cols = new String[dtm.getColumnCount()];
			for (int i = 0; i < dtm.getColumnCount(); i++) {
				cols[i] = dtm.getColumnName(i);
			}
			csv.writeRecord(cols);
			for (int i = 0; i < dtm.getRowCount(); i++) {
				String[] record = new String[dtm.getColumnCount()];
				for (int j = 0; j < dtm.getColumnCount(); j++) {
					Object value = dtm.getValueAt(i, j);

					if (value != null) {
						record[j] = value.toString();
					}
				}
				csv.writeRecord(record);
				csv.flush();
				consoleTextArea.setText("Query output to CSV.");
			}
		} catch (IOException ex) {
			consoleTextArea.setText("Problem writing to CSV: "
					+ ex.getMessage());
		} finally {
			csv.close();
		}
}//GEN-LAST:event_csvButtonActionPerformed

	private void editTextAreaKeyReleased(KeyEvent evt) {//GEN-FIRST:event_editTextAreaKeyReleased
		if (!statementModeButton.isSelected()) {
			return;
		}
		if (evt.getKeyCode() == KeyEvent.VK_ENTER) {
			String statements = editTextArea.getText().trim();
			if (statements.endsWith(";") &&
					editTextArea.getCaretPosition() >= statements.length()) {
				executeButton.doClick();
			}
		}
	}//GEN-LAST:event_editTextAreaKeyReleased

	private void aboutMenuItemActionPerformed(ActionEvent evt) {//GEN-FIRST:event_aboutMenuItemActionPerformed
		new AboutDialog(this, true).setVisible(true);
}//GEN-LAST:event_aboutMenuItemActionPerformed

	private void deleteButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_deleteButtonActionPerformed
		String message =
				"Are you sure you want to clear the database of all\n" +
				"entity types, entities, functions and constraints?";
		String title = "Database Delete Confirmation";
		int opt = JOptionPane.showConfirmDialog(this, message, title, JOptionPane.YES_NO_CANCEL_OPTION);

		switch (opt) {
			case JOptionPane.YES_OPTION:
				d.clear();
				consoleTextArea.setText("Database deleted.");
				break;
			default:
				consoleTextArea.setText("Database not deleted.");
				break;
		}
}//GEN-LAST:event_deleteButtonActionPerformed

	private void jMenuItem2ActionPerformed(ActionEvent evt) {//GEN-FIRST:event_jMenuItem2ActionPerformed
		System.exit(0);
	}//GEN-LAST:event_jMenuItem2ActionPerformed

	private void visualMenuItemActionPerformed(ActionEvent evt) {//GEN-FIRST:event_visualMenuItemActionPerformed
		new DatabaseView(d.getEntityTypes(), this).setVisible(true);
}//GEN-LAST:event_visualMenuItemActionPerformed

	private void loadStatementsButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_loadStatementsButtonActionPerformed
		File f = openFile(this, true,
				new FileNameExtensionFilter("Text Documents (*.txt)", "txt"));

		if (f == null) {
			return;
		}
		
		loadStatements(f);
	}//GEN-LAST:event_loadStatementsButtonActionPerformed

	public void loadStatements(File f) {
		try {
			String text = getFileText(f);
			editTextArea.setText(text);
			editTextArea.setCaretPosition(0);
		} catch (IOException e) {
			consoleTextArea.setText("Error loading file: " + e.getMessage());
		}
	}
	private void openMenuItemActionPerformed(ActionEvent evt) {//GEN-FIRST:event_openMenuItemActionPerformed
		loadButtonActionPerformed(null);
	}//GEN-LAST:event_openMenuItemActionPerformed

	private void saveMenuItemActionPerformed(ActionEvent evt) {//GEN-FIRST:event_saveMenuItemActionPerformed
		dumpButtonActionPerformed(null);
	}//GEN-LAST:event_saveMenuItemActionPerformed

	private void fileMenuActionPerformed(ActionEvent evt) {//GEN-FIRST:event_fileMenuActionPerformed
		deleteButtonActionPerformed(null);
	}//GEN-LAST:event_fileMenuActionPerformed

	private void loadStatementsMenuItemActionPerformed(ActionEvent evt) {//GEN-FIRST:event_loadStatementsMenuItemActionPerformed
		loadStatementsButtonActionPerformed(null);
}//GEN-LAST:event_loadStatementsMenuItemActionPerformed

	private void executeMenuItemActionPerformed(ActionEvent evt) {//GEN-FIRST:event_executeMenuItemActionPerformed
		executeButtonActionPerformed(null);
	}//GEN-LAST:event_executeMenuItemActionPerformed

	private void clearMenuItemActionPerformed(ActionEvent evt) {//GEN-FIRST:event_clearMenuItemActionPerformed
		clearButtonActionPerformed(null);
	}//GEN-LAST:event_clearMenuItemActionPerformed

	private void deleteMenuItemActionPerformed(ActionEvent evt) {//GEN-FIRST:event_deleteMenuItemActionPerformed
		deleteButtonActionPerformed(null);
}//GEN-LAST:event_deleteMenuItemActionPerformed

	public void showConsoleCommands(String text) {
		JTextArea jt = new JTextArea(text);
		jt.setFont(new Font("monospaced", Font.PLAIN, 12));
		jt.setEditable(false);
		jt.setOpaque(false);
		JOptionPane.showMessageDialog(this, jt, "Console Options", JOptionPane.INFORMATION_MESSAGE);
	}
	private void visualButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_visualButtonActionPerformed
		visualMenuItemActionPerformed(null);
	}//GEN-LAST:event_visualButtonActionPerformed

	private String showError(String command, InterpreterException ex) {
		StringBuilder sb = new StringBuilder();
		sb.append(ex.getCommand() + '\n');

		for (int i = 0; i < ex.getColumn() - 1; i++) {
			sb.append(' ');
		}
		int pos = command.indexOf(ex.getCommand());

		if (pos != -1) {
			editTextArea.select(0, pos);
		}
		sb.append('^');
		sb.append("\n\n" + ex.getMessage() + "\n\n");
		sb.append(ex.getExecuted() + " statements were executed.");

		return sb.toString();
	}

	private String showError(String command, ExecuteException ex) {
		String[] commands = command.split("\n");
		StringBuilder sb = new StringBuilder(commands[ex.getLine() - 1] + "\n");

		for (int i = 0; i < ex.getColumn() - 1; i++) {
			sb.append(' ');
		}
		sb.append('^');
		sb.append("\n\n" + ex.getMessage() + "\n\n");
		sb.append("0 statements were executed.");

		return sb.toString();
	}

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private JMenuItem aboutMenuItem;
    private JPanel bottomPanel;
    private JButton clearButton;
    private JMenuItem clearMenuItem;
    private JPanel consolePanel;
    private JScrollPane consoleScrollPane;
    private JTextArea consoleTextArea;
    private JButton csvButton;
    private JButton deleteButton;
    private JMenuItem deleteMenuItem;
    private JButton dumpButton;
    private JEditTextArea editTextArea;
    private JButton executeButton;
    private JMenuItem executeMenuItem;
    private JButton expandButton;
    private JMenu fileMenu;
    private JMenu helpMenu;
    private JMenuItem jMenuItem2;
    private JButton loadButton;
    private JButton loadStatementsButton;
    private JMenuItem loadStatementsMenuItem;
    private JPanel mainPanel;
    private JMenuBar menuBar;
    private JMenuItem openMenuItem;
    private JProgressBar progressBar;
    private JPanel progressPanel;
    private JMenu runMenu;
    private JMenuItem saveMenuItem;
    private Separator separator;
    private JSeparator seperator2;
    private JSeparator seperator3;
    private Separator seperator4;
    private Separator seperator5;
    private JSplitPane splitPane;
    private JToggleButton statementModeButton;
    private JPanel statementsPanel;
    private JButton stopButton;
    private JTabbedPane tabbedPane;
    private JPanel tempPanel;
    private JToolBar toolBar;
    private JMenu toolsMenu;
    private JButton visualButton;
    private JMenuItem visualMenuItem;
    // End of variables declaration//GEN-END:variables

	private class SwingThread extends SwingWorker<Void, Integer> {
		private final QueryResultSet q = new QueryResultSet();
		private final String commands;
		private volatile String message = "";
		private volatile boolean allExecuted = false;
		private volatile boolean allDone = false;
		private final Frame owner;
		private volatile RuntimeException t;
		private final Executor executor = Executors.newSingleThreadExecutor();

		public SwingThread(String commands, Frame owner) {
			this.commands = commands;
			this.owner = owner;
		}

		@Override
		protected Void doInBackground() {
			try {
				monitorProgress();
				xfdm.parseStatements(commands, q);
				message += "All statements executed successfully.";
				allExecuted = true;
			} catch (final InterpreterException ex) {
				message += showError(commands, ex);
			} catch (final ExecuteException ex) {
				message += showError(commands, ex);
			} catch (RuntimeException ex) {
				t = ex;
			} catch (Exception ex) {
				message += ex.getMessage();
			}
			return null;
		}

		private void monitorProgress() {
			executor.execute(new Runnable() {
				public void run() {
					try {
						while (!allDone) {
							Thread.sleep(5);
							publish(xfdm.getExecuted(),
									xfdm.getNumOfStatements());
						}
					} catch (InterruptedException ex) {
						System.err.println("Fucked");
					}
				}
			});
		}

		protected void publish(int progress, int total) {
			progressBar.setString(progress + " statements executed");
			progressBar.setMaximum(total);
			progressBar.setValue(progress);
		}

		@Override
		protected void done() {
			allDone = true;
			if (t != null) {
				throw t;
			}
			progressBar.setIndeterminate(false);
			progressPanel.setVisible(false);
			consoleTextArea.setText(message);
			consoleTextArea.append("\n----------\n");

			String event = null;
			while ((event = q.nextEvent()) != null) {
				consoleTextArea.append(event);
				consoleTextArea.append("\n----------\n");
			}

			tabbedPane.removeAll();
			tables.clear();
			panels.clear();

			if (allExecuted) {
				editTextArea.selectAll();
			}

			if (q.size() == 0) {
				tabbedPane.add("Blank", tempPanel);
				bottomPanel.setVisible(false);
				q.clear();
				splitPane.resetToPreferredSizes();
				return;
			}

			bottomPanel.setVisible(true);
			QueryResult r = null;

			while((r = q.nextQueryResult()) != null) {
				JPanel p = new JPanel(new BorderLayout(5, 5));
				JLabel l = new JLabel(r.getQuery(), 0);
				JTable tb = new NonEditableTable(
						r.getRows(), r.getColumnNames().toArray());
				tb.setRowSorter(new TableRowSorter<TableModel>(tb.getModel()));
				JScrollPane s = new JScrollPane(tb);
				p.add(l, BorderLayout.NORTH);
				p.add(s, BorderLayout.CENTER);
				tables.add(tb);
				panels.add(p);
				tabbedPane.add("Query " + (tabbedPane.getTabCount() + 1), p);
			}
			q.clear();
			splitPane.resetToPreferredSizes();
			editTextArea.requestFocusInWindow();
		}

	}

	private static class NonEditableTable extends JTable {

		public NonEditableTable(Object[][] rowData, Object[] columnNames) {
			super(rowData, columnNames);
		}

		@Override
		public boolean isCellEditable(int row, int col) {
			return false;
		}
	}

	private TransferHandler handler = new TransferHandler() {

		@Override
		public boolean canImport(TransferHandler.TransferSupport support) {
			if (!support.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) {
				return false;
			}
			return true;
		}

		@Override
		public boolean importData(TransferHandler.TransferSupport support) {
			if (!canImport(support)) {
				return false;
			}

			Transferable t = support.getTransferable();
			BufferedReader in = null;
			try {
				List<File> l = (List)t.getTransferData(DataFlavor.javaFileListFlavor);

				if (l.get(0).getName().toLowerCase().endsWith(".xfm")) {
					loadDatabase(l.get(0));
					return true;
				}
				editTextArea.setText(getFileText(l.get(0)));
			} catch (UnsupportedFlavorException e) {
				return false;
			} catch (IOException e) {
				return false;
			}
			return true;
		}
	};

	public String getFileText(File f) throws IOException {
		BufferedReader in = null;
		StringBuilder sb = new StringBuilder();

		try {
			in = new BufferedReader(new FileReader(f));
			String line = null;
			while ((line = in.readLine()) != null) {
				sb.append(line + '\n');
			}
		} finally {
			if (in != null) {
				in.close();
			}
		}

		return sb.toString();
	}
}
